#ifndef GRAPH_H
#define GRAPH_H

#include <cassert>
//#include <stdexcept>

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "Vertex.h"
#include "Edge.h"
using namespace std;


class Edge;
class Vertex;

class Graph{

private:
	vector<Vertex*> vertList;
	vector<Edge*> edgeList;


public:
	Graph(string filename) {
		// This function is the most difficult one. Be carefull.
		// Using ifstream, e.g. ifstream ifs(filename.c_str()); 
		// make new vertices and edges, also fill out the information for them
		// the expected result will be: vertList contains all vertices and edgeList contains all edges. 
		// and the vertices and edges should contains information, e.g. weight, source/end vertex pointer for edge
		
		ifstream ifs(filename.c_str());
		if (!ifs)
		{
			cerr << "File could not be read!";
		}
		else
		{
			int v_no;
			ifs >> v_no;
			//vertList.resize(v_no); //can be broken with invalid file
			
			//The file is processed line by line
			//These vectors represent lines (easier than using strings)
			//vector<double> weights;
			vector< vector<double> > weights; 
			//vector<int> adjacent;
			vector< vector<int> > adjacent;
			
			while(ifs && v_no > 0)
			{
				int v_id, temp = 0;
				
				ifs >> temp;
				
				if (temp != -1)
				{
					ifs.unget();
					temp = 0;
					ifs >> v_id; //vertex id (doubles as line number)
					
					ifs.unget(); //put number back into stream
					bool toggle = true;
					
					int wait = 2;
					vector<int> a;
					vector<double> w;
					
					while( ifs ) //read in data
					{
						ifs >> temp;
						if (temp == -1) break;
						if (toggle || wait > 0)
						{
							//adjacent.push_back(temp);
							a.push_back(temp);
							toggle = false;
							--wait; //wait for first two vertices to be read
						}
						else
						{
							//weights.push_back(temp);
							w.push_back(temp);
							toggle = true;
						}
					}
					weights.push_back(w); //push back lines
					adjacent.push_back(a);
					
					if (v_id != 0)
					{
						Vertex *v;
						v = new Vertex(v_id);
						vertList.push_back(v);
					}
				}
				--v_no;
			} //end while(ifs)
			
			for (int i = 0; i < adjacent.size(); i++) //iterate through lines
			{
				int k = 0; //used to iterate through weights
				for (int j = 1; j < adjacent[i].size(); j++)
				{
					int s = adjacent[i][0] - 1;
					//cout << adjacent[i][0] << '\n';
					int d = 0;
					if (adjacent[i].size() > 1)
					{
						d = adjacent[i][j] - 1;
					}

					Edge *e;
					
					int source = i;
					int dest = 0;
					
					if (!weights.empty() && k < weights[i].size() && adjacent[i].size() > 1) //passed both tests
					{
						e = new Edge(vertList[source], vertList[d], weights[i][k]);
						vertList[source]->enOutList(*e);
						vertList[d]->enInList(*e);
						++k;
					}
					else if (!weights.empty() && k < weights[i].size()) //failed dest test
					{
						e = new Edge(vertList[source], NULL, weights[i][k]); 
						++k;
					}
					else if (adjacent[i].size() > 1) //failed weights test
					{
						e = new Edge(vertList[source], vertList[d], 0.0);
					}
					else //failed both tests
					{
						e = new Edge(vertList[source], NULL, 0.0);
					}
					
					edgeList.push_back(e);
				}
			}
		} //end else	
	} //end Graph

	void PrintGraph() // print out all the edges and associated vertices in the entire graph
	{
		cout<<"Graph is as follows: "<<endl;

        // print all edges and related vertices
		for(int i=0;i<edgeList.size();i++)
		{
			// print out weight of edge
            // and source/end vertex id
			cout << '(' << edgeList[i]->getsVertP()->getID() << "->"
				<< edgeList[i]->geteVertP()->getID() << ") "
				<< "wt: " << edgeList[i]->getWeight() << '\n';
		}

        // print all vertices and related edges
		for(int i=0;i<vertList.size();i++)
		{
			// print out in edges and out edges with weight information
			vertList[i]->printInEdge();
			vertList[i]->printOutEdge();
		}
	}
	
	const vector<Vertex*>& get_vertices() const
	{
		return vertList;
	}
};
#endif